#include "Socket.h"

int Socket::noSockets = 0;

/*
 * Default constructor
 */ 
Socket::Socket(void) {}

/*
 * Windows only startup, MAKEWORD(2,0) specifies the Winsock 
 * version number
 */
void Socket::start()
{
    if(!noSockets) {
		#ifdef WIN32
        WSADATA wsd;
        if(WSAStartup(MAKEWORD(2,2), &wsd)) {
            fprintf(stderr,"Could not start WSA\n");
        }
		#endif
    }
    noSockets++;
}

/*
 * Constructor creates a socket
 */
Socket::Socket(int id_, socketType type_)
{
    this->id = id_;
    this->type = type_;
    start();
    if(type == tcp) {
      s = socket(AF_INET, SOCK_STREAM, 0);
    } else if(type == udp) {
      s = socket(AF_INET, SOCK_DGRAM, 0);
    }
    if(s < 0) {
	     fprintf(stderr,"Could not create socket\n");
         return;
    }
	 refCounter = new int(1);
}

/*
 * Constrtor takes a socket
 */
Socket::Socket(int id_, socketType type_, SOCKET s_)
{
    this->id = id_;
    this->type = type_;
    this->s = s_;
    start();
    refCounter = new int(1);
}

/*
 * Socket destructor
 */
Socket::~Socket()
{
    (*refCounter)--;
    if(!(*refCounter))
    {
        disconnect();
        delete refCounter;
    }
    noSockets--;
    //if(!noSockets)
}

/*
 * Copy constructor, update references and socket counters
 */
Socket::Socket(const Socket& o)
{
    refCounter = o.refCounter;  // take other ref value
    (*refCounter)++;            // increment it
    s = o.s;
    id = o.id;
    noSockets++;
}

/*
 * Assignment operator override 
 */
Socket& Socket::operator=(Socket& o)
{
    (*o.refCounter)++;
    refCounter = o.refCounter;
    s = o.s;
    id = o.id;
    type = o.type;
    noSockets++;
    return *this;
}

/*
 * Equality override
 */
bool Socket::operator ==(Socket& s) {
   return this->id == s.id;
}

/*
 * Not equal override
 */
bool Socket::operator !=(Socket& s) {
   return this->id != s.id;
}

/*
 * Close the socket
 */
void Socket::disconnect()
{
    #ifdef WIN32
        closesocket(s);
        //WSACleanup(); // this kills the server when localhost
    #else
        close(s);
    #endif
}

void Socket::receiveError(int, Socket *){
}

/*
 * Send an unsigned int for TCP socket
 */
int Socket::sendUnsignedInt(Socket *soc, unsigned int data)
{
    //printf("sending %i bytes\n", (int)sizeof(unsigned int));
    return send(soc->s, (char*)&data, sizeof(unsigned int), 0);
    //printf("sent %i bytes\n", res);
}

/*
 * Send a structure for TCP socket
 */
int Socket::sendStruct(Socket *socket, void* data, size_t n)
{
    //printf("sending %i bytes\n", (int)n);
    return send(socket->s, (char*)data, (int)n, 0);
    //printf("sent %i bytes\n", res);
}

/*
 * Receive an unsigned int for TCP socket
 */
int Socket::receiveUnsignedInt(Socket *socket, unsigned int* received)
{
    //printf("receiving %i bytes\n", sizeof(unsigned int));
    return recv(socket->s, (char *)received, sizeof(unsigned int), 0);
    //printf("received %i bytes\n", res);
}

/*
 * Receive a structure for TCP socket
 */
int Socket::receiveStruct(Socket *socket, void* data, size_t n)
{
    //printf("receiving %i bytes\n",(int)n);
    return recv(socket->s, (char *)data, (int)n, 0);
    //printf("received %i bytes\n", res);
}

/*
 * Send a structure, for UDP sockets
 */
int Socket::sendStructTo(Socket *socket, sockaddr_in* addr, void* data, size_t n)
{
    int tolen = sizeof(*addr);
    //printf("sending %i bytes\n", (int)n);
    return sendto(socket->s, (char*)data, (int)n, 0, (sockaddr*)addr, tolen);
    //printf("sent %i bytes\n", res);
}

/*
 * Receive a structure, for UDP sockets
 */
int Socket::receiveStructFrom(Socket *socket, sockaddr_in* addr, void* data, size_t n)
{
    socklen_t fromlen = sizeof(*addr);
    //printf("receiving %i bytes\n",(int)n);
    return recvfrom(socket->s, (char *)data, (int)n, 0, (sockaddr*)addr, &fromlen);
    //printf("received %i bytes\n", res);
}

/*
 * Send a string over TCP socket, append message to length suffix
 */
int Socket::sendString(Socket *socket, char *str)
{
    char msg[MAXMSG];
    sprintf(msg, "%x", (int)strlen(str));
    int l = (int)(strlen(msg) + strlen(str) + 1);
    strcat(msg, " ");
    strcat(msg, str);
    return send(socket->s, msg, l, 0);
}

/*
 * Receive a string over a TCP socket
 */
char* Socket::receiveString(Socket *socket, char *buf)
{
    char c = 'a';
    int r,lengthVal;
    // get the length of the message
    std::string length;
    while(c!=' ') {
        r = recv(socket->s, &c, 1, 0);
        if(r!=1)
        {
            receiveError(r,socket);
            return NULL;
        }
        length += c;
    }
    lengthVal = atoi(length.c_str());
    // get the message
    r = recv(socket->s, buf, lengthVal, 0);
    if(r<=0)
    {
        receiveError(r,socket);
        return NULL;
    }
    buf[r] = '\0';
    return buf;
}

